/*
 *  Copyright 2022 Collate.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  http://www.apache.org/licenses/LICENSE-2.0
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

import { CloseOutlined } from '@mui/icons-material';
import { Button, Card } from 'antd';
import { AxiosError } from 'axios';
import { get } from 'lodash';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { LineageData } from '../../../components/Lineage/Lineage.interface';
import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
import {
  OperationPermission,
  ResourceEntity,
} from '../../../context/PermissionProvider/PermissionProvider.interface';
import { ERROR_PLACEHOLDER_TYPE, SIZE } from '../../../enums/common.enum';
import { EntityType } from '../../../enums/entity.enum';
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
import { Operation } from '../../../generated/entity/policies/policy';
import { EntityReference } from '../../../generated/entity/type';
import { PipelineViewMode } from '../../../generated/settings/settings';
import { TagLabel } from '../../../generated/tests/testCase';
import { TagSource } from '../../../generated/type/tagLabel';
import { EntityData } from '../../../pages/TasksPage/TasksPage.interface';
import { getChartByFqn } from '../../../rest/chartsAPI';
import { getDashboardByFqn } from '../../../rest/dashboardAPI';
import { getDatabaseDetailsByFQN } from '../../../rest/databaseAPI';
import { getDataModelByFqn } from '../../../rest/dataModelsAPI';
import { getDataProductByName } from '../../../rest/dataProductAPI';
import { getDomainByName } from '../../../rest/domainAPI';
import { getGlossaryTermByFQN } from '../../../rest/glossaryAPI';
import { getLineageDataByFQN } from '../../../rest/lineageAPI';
import { getTypeByFQN } from '../../../rest/metadataTypeAPI';
import { getMetricByFqn } from '../../../rest/metricsAPI';
import { getMlModelByFQN } from '../../../rest/mlModelAPI';
import { getPipelineByFqn } from '../../../rest/pipelineAPI';
import { getSearchIndexDetailsByFQN } from '../../../rest/SearchIndexAPI';
import { getContainerByFQN } from '../../../rest/storageAPI';
import { getStoredProceduresByFqn } from '../../../rest/storedProceduresAPI';
import { getTableDetailsByFQN } from '../../../rest/tableAPI';
import { getTopicByFqn } from '../../../rest/topicsAPI';
import {
  DRAWER_NAVIGATION_OPTIONS,
  getEntityLinkFromType,
} from '../../../utils/EntityUtils';
import {
  DEFAULT_ENTITY_PERMISSION,
  getPrioritizedViewPermission,
} from '../../../utils/PermissionsUtils';
import searchClassBase from '../../../utils/SearchClassBase';
import { showErrorToast } from '../../../utils/ToastUtils';
import { useRequiredParams } from '../../../utils/useRequiredParams';
import EntityDetailsSection from '../../common/EntityDetailsSection/EntityDetailsSection';
import { EntityTitleSection } from '../../common/EntityTitleSection/EntityTitleSection';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import Loader from '../../common/Loader/Loader';
import { DataAssetSummaryPanel } from '../../DataAssetSummaryPanel/DataAssetSummaryPanel';
import { DataAssetSummaryPanelV1 } from '../../DataAssetSummaryPanelV1/DataAssetSummaryPanelV1';
import EntityRightPanelVerticalNav from '../../Entity/EntityRightPanel/EntityRightPanelVerticalNav';
import { EntityRightPanelTab } from '../../Entity/EntityRightPanel/EntityRightPanelVerticalNav.interface';
import { SearchedDataProps } from '../../SearchedData/SearchedData.interface';
import CustomPropertiesSection from './CustomPropertiesSection';
import DataQualityTab from './DataQualityTab/DataQualityTab';
import './entity-summary-panel.less';
import { EntitySummaryPanelProps } from './EntitySummaryPanel.interface';
import { LineageTabContent } from './LineageTab';

export default function EntitySummaryPanel({
  entityDetails,
  handleClosePanel,
  highlights,
  isSideDrawer = false,
  panelPath,
  upstreamDepth,
  downstreamDepth,
  pipelineViewMode,
  nodesPerLayer,
}: EntitySummaryPanelProps) {
  // Fallback when tests mock EntityUtils and omit DRAWER_NAVIGATION_OPTIONS
  const NAV_OPTIONS = DRAWER_NAVIGATION_OPTIONS || {
    explore: 'Explore',
    lineage: 'Lineage',
  };
  const { tab } = useRequiredParams<{ tab: string }>();
  const { t } = useTranslation();
  const { getEntityPermission } = usePermissionProvider();
  const [isPermissionLoading, setIsPermissionLoading] =
    useState<boolean>(false);
  const [entityPermissions, setEntityPermissions] =
    useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
  const [activeTab, setActiveTab] = useState<EntityRightPanelTab>(
    EntityRightPanelTab.OVERVIEW
  );
  const [entityData, setEntityData] = useState<any>(null);
  const [entityTypeDetail, setEntityTypeDetail] = useState<any>(null);
  const [isEntityDataLoading, setIsEntityDataLoading] = useState(false);
  const [lineageData, setLineageData] = useState<LineageData | null>(null);
  const [isLineageLoading, setIsLineageLoading] = useState<boolean>(false);
  const [lineageFilter, setLineageFilter] = useState<'upstream' | 'downstream'>(
    'downstream'
  );

  const id = useMemo(() => {
    setIsPermissionLoading(true);

    return entityDetails?.details?.id ?? '';
  }, [entityDetails?.details?.id]);

  const entityType = useMemo(() => {
    return get(entityDetails, 'details.entityType') as EntityType;
  }, [entityDetails]);

  const fetchResourcePermission = async (entityFqn: string) => {
    try {
      setIsPermissionLoading(true);
      const type = (get(entityDetails, 'details.entityType') ??
        ResourceEntity.TABLE) as ResourceEntity;
      const permissions = await getEntityPermission(type, entityFqn);
      setEntityPermissions(permissions);
    } catch {
      // Error
    } finally {
      setIsPermissionLoading(false);
    }
  };
  // Memoize the entity fetch map to avoid recreating it on every render
  const entityFetchMap = useMemo(() => {
    const commonFields = 'owners,domains,tags,extension';

    return {
      [EntityType.TABLE]: (fqn: string) =>
        getTableDetailsByFQN(fqn, { fields: commonFields }),
      [EntityType.TOPIC]: (fqn: string) =>
        getTopicByFqn(fqn, { fields: commonFields }),
      [EntityType.DASHBOARD]: (fqn: string) =>
        getDashboardByFqn(fqn, { fields: commonFields }),
      [EntityType.PIPELINE]: (fqn: string) =>
        getPipelineByFqn(fqn, { fields: commonFields }),
      [EntityType.MLMODEL]: (fqn: string) =>
        getMlModelByFQN(fqn, { fields: commonFields }),
      [EntityType.DATABASE]: (fqn: string) =>
        getDatabaseDetailsByFQN(fqn, { fields: commonFields }),
      [EntityType.DASHBOARD_DATA_MODEL]: (fqn: string) =>
        getDataModelByFqn(fqn, { fields: commonFields }),
      [EntityType.SEARCH_INDEX]: (fqn: string) =>
        getSearchIndexDetailsByFQN(fqn),
      [EntityType.STORED_PROCEDURE]: (fqn: string) =>
        getStoredProceduresByFqn(fqn, { fields: commonFields }),
      [EntityType.CONTAINER]: (fqn: string) =>
        getContainerByFQN(fqn, { fields: commonFields }),
      [EntityType.GLOSSARY_TERM]: (fqn: string) =>
        getGlossaryTermByFQN(fqn, { fields: commonFields }),
      [EntityType.CHART]: (fqn: string) =>
        getChartByFqn(fqn, { fields: commonFields }),
      [EntityType.METRIC]: (fqn: string) =>
        getMetricByFqn(fqn, { fields: commonFields }),
      [EntityType.DATA_PRODUCT]: (fqn: string) =>
        getDataProductByName(fqn, { fields: commonFields }),
      [EntityType.DOMAIN]: (fqn: string) =>
        getDomainByName(fqn, { fields: commonFields }),
    } as Record<string, (fqn: string) => Promise<unknown>>;
  }, []);

  const fetchEntityData = useCallback(async () => {
    if (!entityDetails?.details?.fullyQualifiedName || !entityType) {
      return;
    }

    setIsEntityDataLoading(true);
    try {
      const fqn = entityDetails.details.fullyQualifiedName;
      let entityPromise: Promise<any> | null = null;

      const fetchFn = entityFetchMap[entityType];
      if (fetchFn) {
        entityPromise = fetchFn(fqn);
      }

      if (entityPromise) {
        const data = await entityPromise;
        // Merge API data with essential fields from entityDetails.details
        const mergedData = {
          ...data,
          // Essential fields that are used in DataAssetSummaryPanelV1
          entityType: entityDetails.details.entityType,
          fullyQualifiedName: entityDetails.details.fullyQualifiedName,
          id: entityDetails.details.id,
          description: data.description ?? entityDetails.details.description,
          displayName: entityDetails.details.displayName,
          name: entityDetails.details.name,
          deleted: entityDetails.details.deleted,
          serviceType: (entityDetails.details as any).serviceType,
          service: data.service ?? entityDetails.details.service,
          // Prefer canonical data; fallback to search result if missing
          owners: data.owners ?? entityDetails.details.owners,
          domains: data.domains ?? entityDetails.details.domains,
          tags: data.tags ?? entityDetails.details.tags,
          dataProducts:
            data.dataProducts ?? (entityDetails.details as any).dataProducts,
          tier: (entityDetails.details as any).tier,
          columnNames: (entityDetails.details as any).columnNames,
          database: (entityDetails.details as any).database,
          databaseSchema: (entityDetails.details as any).databaseSchema,
          tableType: (entityDetails.details as any).tableType,
        };
        setEntityData(mergedData);
      }
    } catch (error) {
      showErrorToast(error as AxiosError);
    } finally {
      setIsEntityDataLoading(false);
    }
  }, [entityDetails?.details?.fullyQualifiedName, entityType]);

  const fetchEntityTypeDetail = useCallback(async () => {
    if (!entityType || entityType === EntityType.KNOWLEDGE_PAGE) {
      return;
    }

    try {
      const typeDetail = await getTypeByFQN(entityType);
      setEntityTypeDetail(typeDetail);
    } catch (error) {
      showErrorToast(error as AxiosError);
    }
  }, [entityType]);

  const fetchLineageData = useCallback(async () => {
    const fqn = entityDetails?.details?.fullyQualifiedName;
    if (!fqn || !entityType) {
      return;
    }

    try {
      setIsLineageLoading(true);
      const response = await getLineageDataByFQN({
        fqn,
        entityType,
        config: {
          // When called from lineage view, the parent component passes the user's configured depths.
          upstreamDepth: upstreamDepth ?? 1,
          downstreamDepth: downstreamDepth ?? 1,
          nodesPerLayer: nodesPerLayer ?? 50,
          pipelineViewMode: pipelineViewMode ?? PipelineViewMode.Node,
        },
      });
      setLineageData(response);
    } catch (error) {
      showErrorToast(error as AxiosError);
      setLineageData(null);
    } finally {
      setIsLineageLoading(false);
    }
  }, [entityDetails?.details?.fullyQualifiedName, entityType]);

  const updateEntityData = useCallback(
    (updatedData: Partial<EntityData>) => {
      setEntityData((prevData: EntityData | null) => {
        // Use entityDetails.details as a fallback if prevData is null.
        // This handles the initial update before fetchEntityData completes.
        const baseData = prevData || entityDetails.details;

        // Safety check: If the base data's ID doesn't match the current entity,
        // abort the update to prevent state corruption.
        if (baseData.id !== entityDetails.details.id) {
          return prevData; // Return the original state without changes
        }

        const newState = { ...baseData, ...updatedData };

        return newState;
      });
    },
    [entityDetails.details, fetchEntityData]
  );

  const handleOwnerUpdate = useCallback(
    (owners: EntityReference[]) => {
      updateEntityData({ owners });
    },
    [updateEntityData]
  );

  const handleDomainUpdate = useCallback(
    (domains: EntityReference[]) => {
      updateEntityData({ domains });
    },
    [updateEntityData]
  );

  const handleTagsUpdate = useCallback(
    (updatedTags: TagLabel[]) => {
      // updatedTags from TagsSection only contains classification tags
      const currentTags = entityData?.tags ?? [];
      const glossaryTags = currentTags.filter(
        (tag: TagLabel) => tag.source === TagSource.Glossary
      );
      const tierTags = currentTags.filter((tag: TagLabel) =>
        tag.tagFQN?.startsWith('Tier.')
      );
      updateEntityData({
        tags: [...updatedTags, ...glossaryTags, ...tierTags],
      });
    },
    [entityData, updateEntityData]
  );

  const handleTierUpdate = useCallback(
    (updatedTier?: TagLabel) => {
      const currentTags = entityData?.tags ?? [];
      const tagsWithoutTier = currentTags.filter(
        (tag: TagLabel) => !tag.tagFQN?.startsWith('Tier.')
      );
      const newTags = updatedTier
        ? [...tagsWithoutTier, updatedTier]
        : tagsWithoutTier;
      updateEntityData({ tags: newTags });
    },
    [entityData, updateEntityData]
  );

  const handleDataProductsUpdate = useCallback(
    (updatedDataProducts: EntityReference[]) => {
      updateEntityData({ dataProducts: updatedDataProducts });
    },
    [updateEntityData]
  );

  const handleGlossaryTermsUpdate = useCallback(
    async (updatedTags: TagLabel[]) => {
      // The child component (`GlossaryTermsSection`) now handles the API call.
      // We just need to update the parent's state with the new tags.
      updateEntityData({ tags: updatedTags });
    },
    [updateEntityData]
  );

  const handleDescriptionUpdate = useCallback(
    (updatedDescription: string) => {
      updateEntityData({ description: updatedDescription });
    },
    [updateEntityData]
  );

  useEffect(() => {
    if (id) {
      fetchResourcePermission(id);
    }
  }, [id]);

  // Reset activeTab to OVERVIEW when entity changes
  useEffect(() => {
    setActiveTab(EntityRightPanelTab.OVERVIEW);
  }, [entityDetails?.details?.id]);

  useEffect(() => {
    if (activeTab === EntityRightPanelTab.CUSTOM_PROPERTIES) {
      fetchEntityData();
      fetchEntityTypeDetail();
    } else if (activeTab === EntityRightPanelTab.LINEAGE) {
      fetchLineageData();
    } else if (activeTab === EntityRightPanelTab.OVERVIEW) {
      fetchEntityData();
    }
  }, [activeTab, fetchEntityData, fetchEntityTypeDetail, fetchLineageData]);

  const viewPermission = useMemo(
    () => entityPermissions.ViewBasic || entityPermissions.ViewAll,
    [entityPermissions]
  );

  const summaryComponent = useMemo(() => {
    if (isPermissionLoading) {
      return <Loader />;
    }
    if (!viewPermission) {
      return (
        <ErrorPlaceHolder
          className="border-none h-min-80"
          permissionValue={t('label.view-entity', {
            entity: t('label.data-asset'),
          })}
          size={SIZE.MEDIUM}
          type={ERROR_PLACEHOLDER_TYPE.PERMISSION}
        />
      );
    }
    const type = (get(entityDetails, 'details.entityType') ??
      EntityType.TABLE) as EntityType;
    const entity = entityDetails.details;

    return (
      <DataAssetSummaryPanel
        componentType={tab === NAV_OPTIONS.lineage ? tab : NAV_OPTIONS.explore}
        dataAsset={
          entity as SearchedDataProps['data'][number]['_source'] & {
            dataProducts: DataProduct[];
          }
        }
        entityType={type}
        highlights={highlights}
      />
    );
  }, [tab, entityDetails, viewPermission, isPermissionLoading]);

  const summaryComponentV1 = useMemo(() => {
    if (isPermissionLoading) {
      return <Loader />;
    }
    if (!viewPermission) {
      return (
        <ErrorPlaceHolder
          className="border-none h-min-80"
          permissionValue={t('label.view-entity', {
            entity: t('label.data-asset'),
          })}
          size={SIZE.MEDIUM}
          type={ERROR_PLACEHOLDER_TYPE.PERMISSION}
        />
      );
    }
    const type = (get(entityDetails, 'details.entityType') ??
      EntityType.TABLE) as EntityType;
    const entity = entityData || entityDetails.details;

    return (
      <DataAssetSummaryPanelV1
        componentType={tab === NAV_OPTIONS.lineage ? tab : NAV_OPTIONS.explore}
        dataAsset={
          entity as SearchedDataProps['data'][number]['_source'] & {
            dataProducts: DataProduct[];
          }
        }
        entityType={type}
        highlights={highlights}
        panelPath={panelPath}
        onDataProductsUpdate={handleDataProductsUpdate}
        onDescriptionUpdate={handleDescriptionUpdate}
        onDomainUpdate={handleDomainUpdate}
        onGlossaryTermsUpdate={handleGlossaryTermsUpdate}
        onLinkClick={handleClosePanel}
        onOwnerUpdate={handleOwnerUpdate}
        onTagsUpdate={handleTagsUpdate}
        onTierUpdate={handleTierUpdate}
      />
    );
  }, [
    tab,
    entityDetails,
    entityData,
    viewPermission,
    isPermissionLoading,
    handleOwnerUpdate,
    handleDomainUpdate,
    handleTagsUpdate,
    handleTierUpdate,
    handleDataProductsUpdate,
    handleDescriptionUpdate,
    handleGlossaryTermsUpdate,
  ]);
  const entityLink = useMemo(
    () => searchClassBase.getEntityLink(entityDetails.details),
    [entityDetails, getEntityLinkFromType]
  );

  const handleTabChange = (tab: EntityRightPanelTab) => {
    setActiveTab(tab);
  };

  const renderLineageContent = () => {
    if (isLineageLoading) {
      return (
        <div className="flex-center p-lg">
          <Loader size="default" />
        </div>
      );
    }

    if (lineageData) {
      return (
        <LineageTabContent
          entityFqn={entityDetails?.details?.fullyQualifiedName || ''}
          filter={lineageFilter}
          lineageData={lineageData}
          onFilterChange={setLineageFilter}
        />
      );
    }

    return (
      <div className="text-center text-grey-muted p-lg">
        {t('label.no-data-found')}
      </div>
    );
  };

  const renderTabContent = () => {
    switch (activeTab) {
      case EntityRightPanelTab.OVERVIEW:
        return (
          <>
            {viewPermission && !isSideDrawer && (
              <EntityTitleSection
                className="title-section"
                entityDetails={entityDetails.details}
                entityLink={entityLink}
              />
            )}
            <div className="overview-tab-content">{summaryComponentV1}</div>
          </>
        );
      case EntityRightPanelTab.SCHEMA:
        return (
          <>
            {viewPermission && !isSideDrawer && (
              <EntityTitleSection
                className="title-section"
                entityDetails={entityDetails.details}
                entityLink={entityLink}
              />
            )}
            <div className="entity-summary-panel-tab-content">
              <EntityDetailsSection
                dataAsset={entityDetails.details}
                entityType={entityType}
                highlights={highlights}
                isLoading={isPermissionLoading}
              />
            </div>
          </>
        );
      case EntityRightPanelTab.LINEAGE:
        return (
          <>
            {viewPermission && !isSideDrawer && (
              <EntityTitleSection
                className="title-section"
                entityDetails={entityDetails.details}
                entityLink={entityLink}
              />
            )}
            <div className="entity-summary-panel-tab-content">
              <div className="p-x-md">{renderLineageContent()}</div>
            </div>
          </>
        );
      case EntityRightPanelTab.DATA_QUALITY:
        return (
          <>
            {viewPermission && !isSideDrawer && (
              <EntityTitleSection
                className="title-section"
                entityDetails={entityDetails.details}
                entityLink={entityLink}
              />
            )}
            <DataQualityTab
              entityFQN={entityDetails.details.fullyQualifiedName || ''}
              entityType={entityType}
            />
          </>
        );
      case EntityRightPanelTab.CUSTOM_PROPERTIES: {
        return (
          <>
            {viewPermission && !isSideDrawer && (
              <EntityTitleSection
                className="title-section"
                entityDetails={entityDetails.details}
                entityLink={entityLink}
              />
            )}
            <CustomPropertiesSection
              entityData={entityData}
              entityDetails={entityDetails}
              entityType={entityType}
              entityTypeDetail={entityTypeDetail}
              isEntityDataLoading={isEntityDataLoading}
              viewCustomPropertiesPermission={getPrioritizedViewPermission(
                entityPermissions,
                Operation.ViewCustomFields
              )}
            />
          </>
        );
      }
      default:
        return summaryComponent;
    }
  };

  return (
    <div className="entity-summary-panel-container">
      {isSideDrawer && (
        <div className="d-flex items-center justify-between">
          <EntityTitleSection
            className="drawer-title-section"
            entityDetails={entityDetails.details}
            entityLink={entityLink}
            testId="entity-header-title"
            tooltipPlacement="bottomLeft"
          />
          <Button
            aria-label={t('label.close')}
            className="drawer-close-icon flex-center mr-2"
            data-testid="drawer-close-icon"
            icon={<CloseOutlined />}
            size="small"
            onClick={handleClosePanel}
          />
        </div>
      )}
      <div className="d-flex gap-2 w-full">
        <Card
          bordered={false}
          className={`summary-panel-container ${
            isSideDrawer ? 'drawer-summary-panel-container' : ''
          }`}>
          <Card
            className={`content-area ${
              isSideDrawer ? 'drawer-content-area' : ''
            }`}
            style={{ width: '80%', display: 'block' }}>
            {renderTabContent()}
          </Card>
        </Card>
        <EntityRightPanelVerticalNav
          activeTab={activeTab}
          entityType={entityType}
          isSideDrawer={isSideDrawer}
          onTabChange={handleTabChange}
        />
      </div>
    </div>
  );
}
